/*
 *  Scene.cpp
 *  c_Render
 *
 *  Created by liao peterson on 11/8/09.
 *  Copyright 2009 uestc. All rights reserved.
 *
 */

#include "Scene.h"
#include "Physics.h"

Lamp::Lamp()
{
	loc=(GLfloat*)malloc(sizeof(GLfloat)*3);
	rgb=(GLfloat*)malloc(sizeof(GLfloat)*3);
	this->energy=0;
}

Lamp::Lamp(GLfloat*Nloc,GLfloat*Nrgb,GLfloat Nenergy=1.0)
{
	loc=(GLfloat*)malloc(sizeof(GLfloat)*3);
	rgb=(GLfloat*)malloc(sizeof(GLfloat)*3);
	memcpy(loc, Nloc, sizeof(GLfloat)*3);
	memcpy(rgb, Nrgb, sizeof(GLfloat)*3);
	energy=Nenergy;
}

Lamp::~Lamp()
{
	free(loc);
	free(rgb);
}

//////////////////////////////////////////////////////////////////////////////////////////////////
Camera::Camera()
{
	ratio=0.67;
	// Adjust These two parameters accordingly
	near=-2.0;
	far=-100.0;
	World2Camera=new Matrix4x4;
	//loc=(GLfloat*)malloc(sizeof(GLfloat)*3);
	//dir=(GLfloat*)malloc(sizeof(GLfloat)*3);
	look.z=1;
	//look = new vec4(0, 0, 1);
	//look={0,0,1,1};
}

Camera::Camera(const vec3&Nloc,const vec3&Ndir,GLfloat Nratio=0.67,GLfloat Nnear=-1.0,GLfloat Nfar=-1000.0)
{
	//loc=(GLfloat*)malloc(sizeof(GLfloat)*3);
	//dir=(GLfloat*)malloc(sizeof(GLfloat)*3);
	//memcpy(loc, Nloc, sizeof(GLfloat)*3);
	//memcpy(dir, Ndir, sizeof(GLfloat)*3);
	loc = Nloc;
	dir = Ndir;
	ratio=Nratio;
	near=Nnear;
	far=Nfar;
	World2Camera==new Matrix4x4;
	look.z=1;
	//look = new vec4(0, 0, 1);
	//look={0,0,1,1};
}

Camera::~Camera()
{
	delete World2Camera;
};

void Camera::SetMatrix()
{	
	Matrix4x4 trans,rot,pers;
	calQuat(dir.x, dir.y, dir.z);
	//trans.SetTranslate(-loc[0], -loc[1], -loc[2]);
	trans.SetTranslate(-loc.x,-loc.y,-loc.z);
	rot.SetRotQuat(&quat);
	pers.SetPers(ratio, near, far);
	
	Matrix4x4 matrix=pers*rot*trans;
	//if(World2Camera)delete World2Camera;
	//World2Camera=new Matrix4x4(&matrix);
	World2Camera->Copy4x4(&matrix);
	
	rot.SetRotQuatI(&quat);
	vec4 temp(0,0,-1);
	look = rot * temp;
}

// Only used for finger update in scene.
void Camera::inverseTransform(vec3&pt)
{
	Matrix3x3 rot;
	rot.SetRotQuatI(&quat);
	pt = rot * pt;
}

void Camera::calQuat(GLfloat x,GLfloat y,GLfloat z)
{
	Quat tempQ;
	vec3 tempV = {1, 0, 0};
	if(x != 0){
		tempQ.Set(tempV, x);
		quat = tempQ;
	}
	if(y != 0){
		tempV.setValue(0, 1, 0);
		tempQ.Set(tempV, y);
		quat = tempQ * quat;
	}
	if(z != 0){
		tempV.setValue(0, 0, 1);
		tempQ.Set(tempV, z);
		quat = tempQ * quat;
	}
}

void Camera::updateQuat(GLfloat x,GLfloat y,GLfloat z)
{
	Quat tempQ;
	vec3 tempV = {1, 0, 0};
	if(x != 0){
		tempQ.Set(tempV, x);
		quat = tempQ * quat;
	}
	if(y != 0){
		tempV.setValue(0, 1, 0);
		tempQ.Set(tempV, y);
		quat = tempQ * quat;
	}
	if(z != 0){
		tempV.setValue(0, 0, 1);
		tempQ.Set(tempV, z);
		quat = tempQ * quat;
	}
}

void Camera::rotateAroundAxis(const vec3&axis, GLfloat rot)
{
	Quat qTemp;
	qTemp.Set(axis,rot);
	Matrix3x3 mTemp;
	mTemp.SetRotQuat(&qTemp);
	loc = mTemp * loc;
}

void Camera::lookAt(const vec3&pt)
{
	dir = pt - loc;
	dir.normalize();
}

//////////////////////////////////////////////////////////////////////////////////////////////////
Scene::Scene()
{
	mList=new List<Mesh>;
	cList=new List<Camera>;
	lList=new List<Lamp>;
#ifdef PHYSICS_MODE
	phySim = new physicsSimulator;
#endif
}

Scene::~Scene()
{
	delete mList;
	delete cList;
	delete lList;
#ifdef PHYSICS_MODE
	delete phySim;
#endif
	free(textureIds);
}

void Scene::LoadScene(const GLchar*filename)
{
	FILE*fp=fopen(filename,"r");
	//if(fp==NULL)
	//	perror("Error");
	assert(fp);
	GLint number=0,cnum=0,lnum=0;
	fread(&number,sizeof(GLint),1,fp);
	fread(&cnum,sizeof(GLint),1,fp);
	fread(&lnum,sizeof(GLint),1,fp);
	
	for(int i=0;i<number;i++)
	{
		Mesh*temp=new Mesh;
		
		fread(&temp->vertexCount,sizeof(GLint),1,fp);
		fread(&temp->triangleCount, sizeof(GLint), 1, fp);
		fread(&temp->indexCount, sizeof(GLint), 1, fp);
		fread(&temp->texEnable,sizeof(GLint),1,fp);
		fread(&temp->loc,sizeof(GLfloat),3,fp);
		GLfloat q[4];
		fread(q,sizeof(GLfloat),4,fp);
		temp->quat=new Quat(q);
		fread(temp->size, sizeof(GLfloat), 3, fp);
		temp->SetMatrix();
		temp->vertexArray=(GLfloat**)malloc(sizeof(GLfloat*));
		temp->normalArray=(GLfloat**)malloc(sizeof(GLfloat*));
		temp->vertexArray[0]=(GLfloat*)malloc(sizeof(GLfloat)*9*temp->triangleCount);
		temp->normalArray[0]=(GLfloat*)malloc(sizeof(GLfloat)*9*temp->triangleCount);
		fread(temp->vertexArray[0],sizeof(GLfloat),9*temp->triangleCount,fp);
		fread(temp->normalArray[0],sizeof(GLfloat),9*temp->triangleCount,fp);
		
		
		temp->currentVertex=temp->vertexArray[0];
		temp->currentNormal=temp->normalArray[0];
		
		if(temp->texEnable)
		{
			fread(&temp->nameLength,sizeof(GLint),1,fp);
			temp->filename=(GLchar*)malloc(sizeof(GLchar)*(temp->nameLength+1));
			fread(temp->filename,sizeof(GLchar),temp->nameLength,fp);
			temp->filename[temp->nameLength]='\0';
			temp->uv=(GLfloat*)malloc(sizeof(GLfloat)*6*temp->triangleCount);
			fread(temp->uv,sizeof(GLfloat),6*temp->triangleCount,fp);
		}
		
#ifdef PHYSICS_MODE
		physicsIntegrator*ft=new physicsIntegrator;
		GLint t;
		fread(&t, sizeof(GLint), 1, fp);
		switch (t){
			case 0:
				ft->pi.boundType = BOX;
				ft->pi.bBox = new box;
				fread(&ft->pi.bBox->halfSize.x,sizeof(GLfloat),1,fp);
				fread(&ft->pi.bBox->halfSize.y,sizeof(GLfloat),1,fp);
				fread(&ft->pi.bBox->halfSize.z,sizeof(GLfloat),1,fp);
				break;
			case 1:
				ft->pi.boundType = SPHERE;
				fread(&ft->pi.bRadius, sizeof(GLfloat), 1, fp);
				break;
			case -1:
				ft->pi.boundType = STATIC;
				break;
			default:
				ft->pi.boundType = UNKNOWN;
				break;
		}
		fread(&ft->pi.mass, sizeof(GLfloat), 1,fp);
		ft->target = temp;
		ft->transform = new Matrix4x4(temp->Model2World);
		ft->pi.loc = temp->loc;
		phySim->fList->Append(ft);
#endif
		
		mList->Append(temp);
		//for(int i=0;i<15;i++)
		//	printf("%f ",mList->current->data->Model2World->m[i]);
	}
	
	for(int i=0;i<cnum;i++)
	{
		Camera*tempc=new Camera;
		fread(&tempc->loc,sizeof(GLfloat),3,fp);
		fread(&tempc->dir,sizeof(GLfloat),3,fp);
		tempc->updateQuat(tempc->dir.x, tempc->dir.y, tempc->dir.z);
		
		tempc->SetMatrix();
		cList->Append(tempc);
	}
	for(int i=0;i<lnum;i++)
	{
		GLfloat loc[3];GLfloat rgb[3];GLfloat energy;
		fread(loc,sizeof(GLfloat),3,fp);
		fread(rgb,sizeof(GLfloat),3,fp);
		fread(&energy,sizeof(GLfloat),1,fp);
		Lamp*templ=new Lamp(loc,rgb,energy);
		lList->Append(templ);
	}
}	

/*void Scene::updateFinger(vec3&pt, RenderObject*targets)
 {
 Camera * cam = cList->current->data;
 vec3 dir = {(pt.x*2/X_RESOLUTION - 1) * cam->ratio, -pt.y*2/Y_RESOLUTION + 1, -1.0f};
 cam->inverseTransform(dir);
 dir.normalize();
 vec3 loc = {cam->loc.x, cam->loc.y, cam->loc.z};
 line l = {dir, loc};
 
 GLint index;
 if((index = phySim->updateFinger(l)) != -1)
 {
 mList->Dismiss(index);
 for(int i=index;i<mList->count+1;i++)
 {
 RenderObject*temp = &targets[i];
 targets[i]=targets[i+1];
 delete temp;
 }
 
 phySim->fList->SetCurrent(index);
 phySim->fList->current->data->deleted = 1;
 phySim->fList->Dismiss(index);
 }
 }*/

GLint Scene::updateFinger(vec3&pt)
{
	Camera * cam = cList->current->data;
	vec3 dir = {(pt.x*2/X_RESOLUTION - 1) * cam->ratio, -pt.y*2/Y_RESOLUTION + 1, -1.0f};
	cam->inverseTransform(dir);
	dir.normalize();
	vec3 loc = {cam->loc.x, cam->loc.y, cam->loc.z};
	line l = {dir, loc};
	
	GLint index = phySim->updateFinger(l);
	if(index!=-1)
	{
		mList->Dismiss(index);
		phySim->fList->SetCurrent(index);
		phySim->fList->current->data->deleted = 1;
		phySim->fList->Dismiss(index);
	}
	return index;
}


